package com.qiyue.admin.core.aop;

import com.qiyue.admin.biz.base.log.BizLogBiz;
import com.qiyue.admin.core.model.SystemLogEntity;
import com.qiyue.admin.entity.base.log.BizLogEntity;
import com.qiyue.annotation.SystemServiceLog;
import com.qiyue.common.model.BizResultModel;
import com.qiyue.utils.json.JacksonUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;

/**
 * created by yebinghuan on 2018/3/30
 */
/**
 *  日志系统-代理
 *
 *  切面
 *
 *  目标对象（Target Object）
 *  切入点（Pointcut）
 *  连接点（Joinpoint）
 *  通知（Advice）
 *  切面（Aspect）
 *  ----------------------------------------------------------------------------------------------------
 *  后通知（After advice）
 *  前置通知（Before advice）
 *  返回后通知（After return advice）
 *  抛出异常后通知（After throwing advice）
 *  环绕通知（Around advice）
 *
 *
 */
@Slf4j
@Aspect
@Component

public class BizLogAspect {

    @Autowired
    protected HttpServletRequest request;


    @Autowired
    private BizLogBiz bizLogBiz;


    @Pointcut("@annotation(com.qiyue.annotation.SystemServiceLog)")
    public  void serviceAspect() {

    }


    /**
     * 前置通知
     */
    @Before("serviceAspect()")
    public void beforeMethod() {
//        System.out.println("before method");
    }

    /**
     * value="切入点表达式或命名切入点",
     * pointcut="切入点表达式或命名切入点", 同理， 选用了pointcut 会自动覆盖value
     * argNames="参数列表参数名"
     * returning="返回值对应参数名"
     *
     * @param joinPoint 连接点
     * @param bizResult 返回值
     */
    @AfterReturning(  pointcut = "serviceAspect()" , returning="bizResult" )
    public void doAfterReturnAdvice(JoinPoint joinPoint, BizResultModel bizResult){

        try{
            log.info("=====返回后通知开始=====");

            String method = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()";
            String description = getMethodDescription(joinPoint);


            log.info("请求方法:{}" , method);
            log.info("自定义描述：{}",description);
            log.info("当前用户:{}"); //后续通过jwt 解码获取
            log.info("url:{}");
            log.info("业务名称:{}"); //由url与权限匹配得出
            log.info("操作表名{}");//同上
            log.info("操作类型:{}");

            System.err.println(bizResult.isList());
            BizLogEntity logEntity = new BizLogEntity();
            logEntity.setBizName("测试");
            logEntity.setMethod(method);
            logEntity.setDescription(description);
            logEntity.setTableName("测试");
            logEntity.setOperationType("创建");
            bizLogBiz.saveBizLog(bizResult,logEntity);
//
//            new Thread( ()->{
//
//                try {
//
//                    System.err.println(bizResult.isList());
//                    BizLogEntity logEntity = new BizLogEntity();
//                    logEntity.setBizName("测试");
//                    logEntity.setMethod(method);
//                    logEntity.setDescription(description);
//                    logEntity.setTableName("测试");
//                    logEntity.setOperationType("创建");
//                    bizLogBiz.saveBizLog(bizResult,logEntity);
//                } catch (IllegalAccessException e) {
//                    e.printStackTrace();
//
//                }
//
//            }).start();


        }catch (Exception e){
            log.error("==返回通知异常==");
            log.error("异常信息:{}", e.getMessage());
        }


    }








    /**
     * 异常通知 用于拦截service层记录异常日志
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "serviceAspect()", throwing = "e")
    public  void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpSession session = request.getSession();
        //读取session中的用户
        //User user = (User) session.getAttribute(WebConstants.CURRENT_USER);
        //获取请求ip
        String ip = request.getRemoteAddr();
        //获取用户请求方法的参数并序列化为JSON格式字符串
        String params = "";
        if (joinPoint.getArgs() !=  null && joinPoint.getArgs().length > 0) {
            for ( int i = 0; i < joinPoint.getArgs().length; i++) {
                params += JacksonUtil.toJSon(joinPoint.getArgs()[i]);
            }
        }
        try {
            /*========控制台输出=========*/
            System.out.println("=====异常通知开始=====");
            System.out.println("异常代码:" + e.getClass().getName());
            System.out.println("异常信息:" + e.getMessage());
            System.out.println("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
            System.out.println("方法描述:" + getServiceMthodDescription(joinPoint));
            System.out.println("请求人:" + "测试");
            System.out.println("请求IP:" + ip);
            System.out.println("请求参数:" + params);
            /*==========数据库日志=========*/
            SystemLogEntity log=new SystemLogEntity();
            log.setMethod(joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()");//请求方法
            log.setDescription(getServiceMthodDescription(joinPoint));//方法描述
            log.setIp(ip);//IP
            log.setParams( params);
            log.setException(e.getClass().getName());
            //保存数据库
//            systemLogService.saveLog(log);
            System.out.println("=====异常通知结束=====");
        }  catch (Exception ex) {
            //记录本地异常日志
            log.error("==异常通知异常==");
            log.error("异常信息:{}", ex.getMessage());
        }
        /*==========记录本地异常日志==========*/
        log.error("异常方法:{}异常代码:{}异常信息:{}参数:{}", joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName(), e.getClass().getName(), e.getMessage(), params);

    }


    /**
     * 获取注解中对方法的描述信息 用于service层注解
     *
     * @param joinPoint 切点
     * @return 方法描述
     * @throws Exception
     */
    public  static String getServiceMthodDescription(JoinPoint joinPoint)
            throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        String description = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    description = method.getAnnotation(SystemServiceLog.class).description();
                    break;
                }
            }
        }
        return description;
    }

    /**
     * 用反射获取自定义注解中的 参数
     * @param joinPoint 切点
     * @return 方法描述
     * @throws Exception
     */
    public  static String getMethodDescription(JoinPoint joinPoint)  throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        String description = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    description = method.getAnnotation(SystemServiceLog.class).description();
                    break;
                }
            }
        }
        return description;
    }
}
